using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace PacCraft
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        bool debugMode = true;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Player player;
        Map map;
        Text firstLine;
        Text secondLine;
        Text prism;
        List<Monster> monsters;

        Song backgroundMusic;

        int nbMonster = 3;

        // Score
        int lastReset = 0;
        int score = 0;
        bool showScore = false;

        // Distance
        int everyEach = 200;
        int lastBop= 0;
        SoundEffect bop;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 600;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            firstLine = new Text(Content.Load<SpriteFont>("Digital"), "Find the", true);
            secondLine = new Text(Content.Load<SpriteFont>("Digital-mini"), "", true);
            prism = new Text(Content.Load<SpriteFont>("Digital"), "prism", true);
            bop = Content.Load<SoundEffect>("Bop");
            prism.position.X += 335;
            reset();
            playBackgroundMusic();
            if (debugMode)
                MediaPlayer.Stop();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        private void playBackgroundMusic()
        {
            backgroundMusic = Content.Load<Song>("background");
            MediaPlayer.IsRepeating = true;
            MediaPlayer.Play(backgroundMusic);
            MediaPlayer.Volume = 0.3f;
        }

        private void reset()
        {
            map = new Map(Content.Load<Model>("Box"), Content.Load<Model>("Prism"));
            player = new Player(graphics, map, Content.Load<Model>("Carl"));

            List<Node> nodes = new List<Node>();
            nodes = map.graph.getPath(new Vector2((int)player.getPosition().X, (int)player.getPosition().Y), map.getPrismPosition());
            if (nodes.Count() < 10)
                reset();
            monsters = new List<Monster>();
            for (int i = 0; i < nbMonster; ++i)
            {
                Monster m = new Monster(Content.Load<Model>("Monster"), map, map.graph);
                nodes = map.graph.getPath(new Vector2((int)player.getPosition().X, (int)player.getPosition().Y), new Vector2((int)m.getPosition().X, (int)m.getPosition().Y));
                if (nodes.Count() < 15)
                    --i;
                else
                    monsters.Add(m);
            }
            MediaPlayer.Stop();
        }

        private void lostGame(GameTime gameTime)
        {
            score = (int)gameTime.TotalGameTime.TotalSeconds - lastReset;
            lastReset = (int)gameTime.TotalGameTime.TotalSeconds;
            firstLine.reset("");
            prism.reset("Game over!");
            prism.position = new Vector2(200, 200);
            secondLine.position = new Vector2(230, 300);
            secondLine.reset("              Time alive: " + score.ToString() + '\n' + "Press SPACE KEY to play again");
            reset();
            showScore = true;
        }

        private void newGame(GameTime gameTime)
        {
            score = (int)gameTime.TotalGameTime.TotalSeconds - lastReset;
            if (score > 300)
                score = 0;
            else
                score = 300 - score;
            lastReset = (int)gameTime.TotalGameTime.TotalSeconds;
            prism.reset("Bravo!");
            prism.position = new Vector2(100, 250);
            secondLine.position = new Vector2(360, 270);
            firstLine.reset("");
            secondLine.reset("Score: " + score.ToString() + '\n' +  "Press SPACE KEY to play again");
            reset();
            showScore = true;
        }

        private void playBop()
        {
            Vector2 playerPosition = player.getPosition();
            Vector2 prismPosition = map.getPrismPosition();

            everyEach = (int)Math.Sqrt(Math.Pow(playerPosition.X - prismPosition.X, 2) + Math.Pow(playerPosition.Y - prismPosition.Y, 2));
            if (everyEach < 7)
                everyEach = 7;
            lastBop++;
            if (everyEach < lastBop)
            {
                bop.Play();
                lastBop = 0;
            }
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
                player.move(Keys.Right);
            if (Keyboard.GetState().IsKeyDown(Keys.Left))
                player.move(Keys.Left);
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
                player.move(Keys.Up);
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
                player.move(Keys.Down);
            if (Keyboard.GetState().IsKeyDown(Keys.Space) && showScore)
            {
                showScore = false;
                MediaPlayer.Play(backgroundMusic);
            }
            if (Keyboard.GetState().IsKeyDown(Keys.D))
                player.debug = (player.debug) ? (false) : (true);
            if (Keyboard.GetState().IsKeyDown(Keys.F1))
                player.changeView();
            Vector2 position = player.getPosition();
            if (map.map[(int)position.X][(int)position.Y] == '2')
                newGame(gameTime);
            if (!debugMode)
                playBop();

            foreach(Monster monster in monsters)
                if (monster.move(player.getPosition(), player.getBox()))
                    lostGame(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            if (showScore || gameTime.TotalGameTime.TotalMilliseconds < 1600)
            {
                spriteBatch.Begin();
                firstLine.Draw(spriteBatch);
                secondLine.Draw(spriteBatch);
                prism.Draw(spriteBatch, true);
                spriteBatch.End();
                GraphicsDevice.BlendState = BlendState.Opaque;
                GraphicsDevice.DepthStencilState = DepthStencilState.Default;
                GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
            }
            else
            {
                map.Draw(player);
                player.Draw();
                foreach (Monster monster in monsters)
                    monster.Draw(player);
                //                monster.DrawBox(player.getCamera());
            }
            base.Draw(gameTime);
        }
    }
}
